using System;
using System.Runtime.InteropServices;
 
using Gtk;
using Gdk;

using SharpKnocking.Common;
using SharpKnocking.Common.Widgets;
using SharpKnocking.Common.Widgets.CommonDialogs;
 
namespace SharpKnocking.Doorman
{
	public class TrayIcon
	{
		[Glade.WidgetAttribute]
		Menu trayMenu;		
		
		MainWindow window;
		
		private bool visible;
	
		public TrayIcon()
		{
			Glade.XML gxml = new Glade.XML (null, "gui.glade", "trayMenu", null);
			gxml.Autoconnect (this);
			
			
		
			InitializeWidgets();
			
			this.window = new MainWindow();
			
			visible = true;
							
		}
		
		#region Private methods
		
		private void InitializeWidgets()
		{
			/* in order to receive signals, we need a eventbox,
	                because Gtk.Image doesn't receive signals */
			EventBox eb = new EventBox();					
			eb.Add(new Gtk.Image(ImageResources.SharpKnockingIcon22));
			// hooking event
			eb.ButtonPressEvent += new ButtonPressEventHandler (this.OnImageClick);
			TrayHelper icon = new TrayHelper("Doorman");
			icon.Add(eb);
			// showing the trayicon
			icon.ShowAll();
		}
	
		private void OnImageClick (object o, ButtonPressEventArgs args) // handler for mouse click
		{
	   		if (args.Event.Button == 3) //right click
	   		 {
	      		try
	      		{
					trayMenu.ShowAll();
		      		trayMenu.Popup(	      		
		      			null, null, null, 
		      			IntPtr.Zero, args.Event.Button, args.Event.Time);
				}
				catch(Exception){}
	   		}
	   		else if(args.Event.Button == 1) // Left click
	   		{
	   			// We set the new state;
	   			visible = !visible;
	   			if(visible)
	   				window.Show();
	   			else
	   				window.Hide();
	   			
	   				
	   			
	   		}
		}
		
		private void OnQuitItemActivated(object o, EventArgs args)
		{
			window.Quit();
		}
		
		private void OnRestoreItemActivated(object o, EventArgs args)
		{
			window.Show();
		}
		
		#endregion Private methods
		
		#region Helpers
		
		/// <summary>
		/// This class helps creating the system tray icon.
		/// It uses code from http://www.mono-project.com/GtkSharpNotificationIcon.
		/// </summary>
		class TrayHelper : Plug
		{
			int stamp;
			Orientation orientation;
			
			int selection_atom;
			int manager_atom;
			int system_tray_opcode_atom;
			int orientation_atom;
			IntPtr manager_window;
			FilterFunc filter;
			
			public TrayHelper (string name)
			{
				Title = name;
				stamp = 1;
				orientation = Orientation.Horizontal;
				AddEvents ((int)EventMask.PropertyChangeMask);
				filter = new FilterFunc (ManagerFilter);
				
			}
	 
			protected override void OnRealized ()
			{
				base.OnRealized ();
				Display display = Screen.Display;
				IntPtr xdisplay = gdk_x11_display_get_xdisplay (display.Handle);
				selection_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_S" + Screen.Number.ToString (), false);
				manager_atom = XInternAtom (xdisplay, "MANAGER", false);
				system_tray_opcode_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_OPCODE", false);
				orientation_atom = XInternAtom (xdisplay, "_NET_SYSTEM_TRAY_ORIENTATION", false);
				UpdateManagerWindow ();
				//Screen.RootWindow.AddFilter (filter);
			}
	 
			protected override void OnUnrealized ()
			{
				if (manager_window != IntPtr.Zero)
				{
					Gdk.Window gdkwin =
						Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
						
					//gdkwin.RemoveFilter (filter);
				}
				
				//Screen.RootWindow.RemoveFilter (filter);
				base.OnUnrealized ();
			}
	 
			private void UpdateManagerWindow ()
			{
				IntPtr xdisplay = gdk_x11_display_get_xdisplay (Display.Handle);
				if (manager_window != IntPtr.Zero)
				{
					Gdk.Window gdkwin = Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
					//gdkwin.RemoveFilter (filter);
				}
	 
				XGrabServer (xdisplay);
	 
				manager_window = XGetSelectionOwner (xdisplay, selection_atom);
				if (manager_window != IntPtr.Zero)
					XSelectInput (xdisplay, manager_window, EventMask.StructureNotifyMask | EventMask.PropertyChangeMask);
				XUngrabServer (xdisplay);
				XFlush (xdisplay);
	 
				if (manager_window != IntPtr.Zero) {
					Gdk.Window gdkwin = Gdk.Window.LookupForDisplay (Display, (uint)manager_window);
					//gdkwin.AddFilter (filter);
					SendDockRequest ();
					//GetOrientationProperty ();
				}
			}
	 
			private void SendDockRequest ()
			{
				SendManagerMessage (SystemTrayMessage.RequestDock, manager_window, Id, 0, 0);
			}
	 
			private void SendManagerMessage (SystemTrayMessage message, IntPtr window, uint data1, uint data2, uint data3)
			{
				XClientMessageEvent ev = new XClientMessageEvent ();
				IntPtr display;
	 
				ev.type = XEventName.ClientMessage;
				ev.window = window;
				ev.message_type = (IntPtr)system_tray_opcode_atom;
				ev.format = 32;
				ev.ptr1 = gdk_x11_get_server_time (GdkWindow.Handle);
				ev.ptr2 = (IntPtr)message;
				ev.ptr3 = (IntPtr)data1;
				ev.ptr4 = (IntPtr)data2;
				ev.ptr5 = (IntPtr)data3;
	 
				display = gdk_x11_display_get_xdisplay (Display.Handle);
				gdk_error_trap_push ();
				XSendEvent (display, manager_window, false, EventMask.NoEventMask, ref ev);
				gdk_error_trap_pop ();
			}
	 
			private FilterReturn ManagerFilter (IntPtr xevent, Event evnt)
			{				
				return FilterReturn.Continue;
			}
	 
	 		#region Imports
	 		
			[DllImport ("gdk-x11-2.0")]
			static extern IntPtr gdk_x11_display_get_xdisplay (IntPtr display);
			[DllImport ("gdk-x11-2.0")]
			static extern IntPtr gdk_x11_get_server_time (IntPtr window);
			[DllImport ("gdk-x11-2.0")]
			static extern void gdk_error_trap_push ();
			[DllImport ("gdk-x11-2.0")]
			static extern void gdk_error_trap_pop ();
			
			[DllImport ("libX11", EntryPoint="XInternAtom")]
			extern static int XInternAtom(IntPtr display, string atom_name, bool only_if_exists);
			[DllImport ("libX11")]
			extern static void XGrabServer (IntPtr display);
			[DllImport ("libX11")]
			extern static void XUngrabServer (IntPtr display);
			[DllImport ("libX11")]
			extern static int XFlush (IntPtr display);
			[DllImport ("libX11")]
			extern static IntPtr XGetSelectionOwner (IntPtr display, int atom);
			[DllImport ("libX11")]
			extern static IntPtr XSelectInput (IntPtr window, IntPtr display, EventMask mask);
			[DllImport ("libX11", EntryPoint="XSendEvent")]
			extern static int XSendEvent(IntPtr display, IntPtr window, bool propagate, EventMask event_mask, ref XClientMessageEvent send_event);
			
			#endregion Imports
		}
	 
	 	#region Imported Enums & Structs
	 	
		[Flags]
		internal enum EventMask {
			NoEventMask             = 0,
			KeyPressMask            = 1<<0,
			KeyReleaseMask          = 1<<1,
			ButtonPressMask         = 1<<2,
			ButtonReleaseMask       = 1<<3,
			EnterWindowMask         = 1<<4,
			LeaveWindowMask         = 1<<5,
			PointerMotionMask       = 1<<6,
			PointerMotionHintMask   = 1<<7,
			Button1MotionMask       = 1<<8,
			Button2MotionMask       = 1<<9,
			Button3MotionMask       = 1<<10,
			Button4MotionMask       = 1<<11,
			Button5MotionMask       = 1<<12,
			ButtonMotionMask        = 1<<13,
			KeymapStateMask         = 1<<14,
			ExposureMask            = 1<<15,
			VisibilityChangeMask    = 1<<16,
			StructureNotifyMask     = 1<<17,
			ResizeRedirectMask      = 1<<18,
	                SubstructureNotifyMask  = 1<<19,
			SubstructureRedirectMask= 1<<20,
			FocusChangeMask         = 1<<21,
			PropertyChangeMask      = 1<<22,
			ColormapChangeMask      = 1<<23,
			OwnerGrabButtonMask     = 1<<24
		}
	 
		internal enum SystemTrayMessage
		{
			RequestDock,
			BeginMessage,
			CancelMessage
		}
	 
		internal enum SystemTrayOrientation
		{
			Horz,
			Vert
		}
		
		[StructLayout(LayoutKind.Sequential)]
		internal struct XClientMessageEvent {
			internal XEventName     type;
			internal IntPtr         serial;
			internal bool           send_event;
			internal IntPtr         display;
			internal IntPtr         window;
			internal IntPtr         message_type;
			internal int            format;
			internal IntPtr         ptr1;
			internal IntPtr         ptr2;
			internal IntPtr         ptr3;
			internal IntPtr         ptr4;
			internal IntPtr         ptr5;
		}
	 
		internal enum XEventName {
			KeyPress                = 2,
			KeyRelease              = 3,
			ButtonPress             = 4,
			ButtonRelease           = 5,
			MotionNotify            = 6,
			EnterNotify             = 7,
			LeaveNotify             = 8,
			FocusIn                 = 9,
			FocusOut                = 10,
			KeymapNotify            = 11,
			Expose                  = 12,
			GraphicsExpose          = 13,
			NoExpose                = 14,
			VisibilityNotify        = 15,
			CreateNotify            = 16,
			DestroyNotify           = 17,
			UnmapNotify             = 18,
			MapNotify               = 19,
			MapRequest              = 20,
			ReparentNotify          = 21,
			ConfigureNotify         = 22,
			ConfigureRequest        = 23,
			GravityNotify           = 24,
			ResizeRequest           = 25,
			CirculateNotify         = 26,
			CirculateRequest        = 27,
			PropertyNotify          = 28,
			SelectionClear          = 29,
			SelectionRequest        = 30,
			SelectionNotify         = 31,
			ColormapNotify          = 32,
			ClientMessage           = 33,
			MappingNotify           = 34,
			TimerNotify             = 100,
			
			LASTEvent
		}
		
		#endregion Imported Enums & structs
		
		#endregion Helpers
	}
}